/*  File Name	: SignalHanlder.cpp
 *  Author		: Pang Jiacai
 *	 E-mail		: 823960168@qq.com
 *  Created on	: 2012-4-12
 *  Description:
 */

#include "SignalHanlder.h"
#include "EventHandler.h"
#include "ReactorImpl.h"
#include "SocketPair.h"

EventHandler* SignalHanlder::signal_handlers_[_NSIG]={0};

//sig_atomic_t SignalHanlder::sig_pending_ = 0;

int SignalHanlder::signal_notify_fd[2]={-1};

SignalHanlder::SignalHanlder()
{

}

SignalHanlder::~SignalHanlder()
{

}

int SignalHanlder::open(ReactorImpl *reactor)
{
	this->reactor_ = reactor;

	if(NULL==this->reactor_)
	{
		return -1;
	}
	int result = 0;
	if (signal_notify_fd[0] < 0)
	{
		result = SocketPair::socketpair(signal_notify_fd);
		if (result != 0)
		{
			return result;
		}
	}

	if (signal_notify_fd[0] > 0)
	{
		SocketPair::socketpair_set_nonblock(signal_notify_fd[0]);
		SocketPair::socketpair_set_reuse(signal_notify_fd[0]);
	}
	if (signal_notify_fd[1] > 0)
	{
		SocketPair::socketpair_set_nonblock(signal_notify_fd[1]);
		SocketPair::socketpair_set_reuse(signal_notify_fd[1]);
	}

	//register to reactor
	int fd = signal_notify_fd[0];
	if (this->reactor_->register_handler(fd,&event_handler_,EventHandler::READ_MASK) != 0)
	{
		this->close_notify_fd();
		return -1;
	}
	return 0;
}

void SignalHanlder::close_notify_fd(void)
{
	SocketPair::socketpair_close(this->signal_notify_fd[0]);
	SocketPair::socketpair_close(this->signal_notify_fd[1]);
	SocketPair::socketpair_fd_reset(this->signal_notify_fd);
}

void SignalHanlder::dispatch (int signum, siginfo_t *info,void *p)
{
	if(signum==SIGTRAP)
	{
		return ;
	}
	int sig[1]={0};
	sig[0]=signum;
	SocketPair::socketpair_notify(signal_notify_fd[1],(char*)sig,sizeof(int));
}

int SignalHanlder::register_signal(int signum,EventHandler *sh,
		bool replace_old,EventHandler *old_sh)
{
	if(!this->is_legal_signal(signum) || NULL==sh)
	{
		return -1;
	}
	EventHandler *old_hanlder = this->signal_handlers_[signum];
	if(NULL!=old_hanlder && !replace_old)
	{
		return -1;
	}
	else if(NULL!=old_sh)
	{
		old_sh = old_hanlder;
	}
	this->signal_handlers_[signum] = sh;
	struct sigaction sa;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = SA_SIGINFO;
	sa.sa_sigaction = this->dispatch;
	return ::sigaction(signum,&sa,&sa);
}

int SignalHanlder::remove_signal(int signum,EventHandler *sh)
{
	if(!this->is_legal_signal(signum)
			|| this->signal_handlers_[signum]!=sh)
	{
		return -1;
	}

	this->signal_handlers_[signum] = 0;

	struct sigaction sa;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
	sa.sa_sigaction = NULL;
	return ::sigaction(signum,&sa,&sa);
}

int SignalHanlder::dispatch_signal_handler(int signum)
{
	if(SignalHanlder::is_legal_signal(signum))
	{
		EventHandler *sh = SignalHanlder::signal_handlers_[signum];
		if(NULL!=sh)
		{
			if(sh->handle_signal(signum)==-1)
			{
				SignalHanlder::signal_handlers_[signum] = NULL;
				struct sigaction sa;
				sigemptyset(&sa.sa_mask);
				sa.sa_flags = 0;
				sa.sa_sigaction = NULL;
				::sigaction(signum,&sa,&sa);
				sh->handle_close(signum,EventHandler::SIGNAL_MASK);
			}
		}
	}
	return 1;
}

int SignalHanlder::notify_fd(void)
{
	return this->signal_notify_fd[0];
}

EventHandler *SignalHanlder::handler(int signum)
{
	if(!this->is_legal_signal(signum))
	{
		return NULL;
	}
	return this->signal_handlers_[signum];
}

bool SignalHanlder::is_legal_signal(int signum)
{
	if(signum >0 && signum <_NSIG)
	{
		return true;
	}
	return false;
}

//int SignalHanlder::sig_pending (void)
//{
//	return SignalHanlder::sig_pending_;
//}
//
//void SignalHanlder::sig_pending (int pending)
//{
//	SignalHanlder::sig_pending_ = pending;
//}
